package com.jiazhong.example.project.bookstore.front.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jiazhong.example.project.bookstore.commons.bean.Seckill;
import com.jiazhong.example.project.bookstore.commons.config.JwtConfig;
import com.jiazhong.example.project.bookstore.commons.mapper.SeckillMapper;
import com.jiazhong.example.project.bookstore.commons.util.DateUtil;
import com.jiazhong.example.project.bookstore.commons.util.JsonResult;
import com.jiazhong.example.project.bookstore.commons.util.ResultTool;
import com.jiazhong.example.project.bookstore.front.service.SeckillService;
import io.jsonwebtoken.Claims;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

@Slf4j
@Service
public class SeckillServiceImpl extends ServiceImpl<SeckillMapper, Seckill> implements SeckillService {
    @Resource
    private SeckillMapper seckillMapper;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    // 所有秒杀信息 hash
    public static final String SECKILL_KEY = "seckill";
    // 某个秒杀的库存信息 value
    public static final String SECKILL_KEY_PREFIX = "seckill_Prefix_";
    // 秒杀成功后记录用户信息 set
    public static final String SECKILL_KEY_USER = "seckill_user_";

    @Cacheable(cacheNames = "loadSeckill")
    public JsonResult loadSeckill() {
        HashOperations<String, Object, Object> forHash = stringRedisTemplate.opsForHash();
        ValueOperations<String, String> forValue = stringRedisTemplate.opsForValue();
        List<Seckill> list = list();
        for (Seckill seckill : list) {
            forHash.put(SECKILL_KEY, seckill.getId(), JSONArray.toJSONString(seckill));
            forValue.set(SECKILL_KEY_PREFIX + seckill.getId(), seckill.getSeckillNumber() + "");
        }
        return ResultTool.success(list);
    }

    @Override
    public JsonResult seckill(String seckillId, HttpServletRequest request) {
        log.info("开始进行秒杀操作");
        HashOperations<String, Object, Object> forHash = stringRedisTemplate.opsForHash();
        ValueOperations<String, String> forValue = stringRedisTemplate.opsForValue();
        SetOperations<String, String> forSet = stringRedisTemplate.opsForSet();

        Object o = forHash.get(SECKILL_KEY, seckillId);
        log.info("o:{}", o);
        Seckill seckill = null;
        if (o != null) {
            seckill = JSONArray.parseObject(o.toString(), Seckill.class);
        }
        log.info("seckill:{}", seckill);
        // 判断该商品秒杀是否开始 是否结束
        Date beginTime = DateUtil.parseDate(seckill.getBeginTime());
        Date endTime = DateUtil.parseDate(seckill.getEndTime());
        Date nowDate = new Date();
        log.info("nowDate:{},秒杀开始时间:{},秒杀结束时间:{}", nowDate, beginTime, endTime);
        if (nowDate.before(beginTime)) {
            log.info("秒杀还没有开始");
            return ResultTool.fail(602, "秒杀还没有开始");
        }
        if (nowDate.after(endTime)) {
            log.info("秒杀已经结束");
            return ResultTool.fail(603, "秒杀已经结束");
        }
        // 秒杀的数量书否充足
        int number = Integer.parseInt(forValue.get(SECKILL_KEY_PREFIX + seckillId));
        log.info("number:{}", number);
        if (number <= 0) {
            log.info("很遗憾，没有抢到!");
            return ResultTool.fail(602, "很遗憾，没有抢到!");
        }
        // 一人之能抢购一次
        String token = request.getHeader("token");
        Claims claims = JwtConfig.parseJWT(token);
        String userId = claims.get("id").toString();
        Boolean member = forSet.isMember(SECKILL_KEY_USER + seckillId, userId);
        log.info("member:{}", member);
        if (Boolean.TRUE.equals(member)) {
            log.info("你已经抢购成功了，请给别人一次机会吧!");
            return ResultTool.fail(603, "你已经抢购成功了，请给别人一次机会吧!");
        }
        // 开始抢购
        // 开启watch监控锁
        stringRedisTemplate.watch(SECKILL_KEY_PREFIX + seckillId);
        // 事务组块
        SessionCallback<List<String>> sessionCallback = new SessionCallback() {
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                // 1.开启事务
                stringRedisTemplate.multi();
                // 2.减库存
                forValue.decrement(SECKILL_KEY_PREFIX + seckillId);
                // 3.追加购买记录
                forSet.add(SECKILL_KEY_USER + seckillId, userId);
                // 4.执行事务
                return stringRedisTemplate.exec();
            }
        };
        // 获取到事务执行结果
        List<String> executes = stringRedisTemplate.execute(sessionCallback);
        log.info("executes:{}", executes);
        // 根据执行结果分别执行其他操作
        if (executes.isEmpty() || executes.get(0) == null) {
            // 抢购失败
            return ResultTool.fail(604, "很遗憾，没有抢到!");
        }
        // 抢购成功
        // 这里还会执行其他操作 付款
        return ResultTool.success("恭喜你抢购成功");
    }
}
